package va_btree;
import btree.*;
import global.*;
import va.BitSet;
import va.Range;
import va.VAEntry;
import va_file.VAFile;

import java.io.IOException;

public class VABTreeFile extends BTreeFile {

	public static boolean quietMode = true;
	
    //I think pageNo and slotNo are just for testing
    public static int pageNo=0;
    public static int slotNo=0;

    //Number of bits for each b-tree entry
    public int noOfBits;

    /*
     * Constructor
     * number of bits defaults to whatever the current bits-per-dimension value is set to
     */
    public VABTreeFile(String filename)
            throws GetFileEntryException,
            ConstructPageException,
            IOException,
            AddFileEntryException
    {
        super(filename, AttrType.attrString, 100 * VAFile.BITS_PER_DIMENSTION + 2, DeleteFashion.FULL_DELETE); // + 2 found by experimentation
        this.noOfBits = VAFile.BITS_PER_DIMENSTION;
    }

    public VABTreeFile(String filename, int numberOfBits)
            throws GetFileEntryException,
            ConstructPageException,
            IOException,
            AddFileEntryException
    {
        super(filename, AttrType.attrString, 100 * numberOfBits + 2, DeleteFashion.FULL_DELETE); // + 2 found by experimentation
        this.noOfBits = numberOfBits;
    }

    /*
     * Insert a VA entry into the VA b-tree.
     * We have to convert the vector approximation into a string and add this string as the key.
     */
    public void insert(VAEntry entry)
            throws KeyTooLongException,
            KeyNotMatchException,
            LeafInsertRecException,
            IndexInsertRecException,
            ConstructPageException,
            UnpinPageException,
            PinPageException,
            NodeNotMatchException,
            ConvertException,
            DeleteRecException,
            IndexSearchException,
            IteratorException,
            LeafDeleteException,
            InsertException,
            IOException
    {
        String searchKey = VAEntry.GetApproximationString(entry.approximation, this.noOfBits);

        if (!quietMode) System.out.println("The search key is " + searchKey);
        StringKey key = new StringKey(searchKey);
        super.insert(key, entry.rid);
        if (!quietMode) System.out.println("Insert into B-Tree succsessful");
    }

    /*
     * Deletes a VA entry from the binary search tree
     */

    public void deleteEntry(VAEntry entry) throws UnpinPageException, DeleteRecException, IOException, IndexSearchException, RedistributeException, PinPageException, FreePageException, DeleteFashionException, LeafRedistributeException, IndexInsertRecException, IndexFullDeleteException, InsertRecException, KeyNotMatchException, LeafDeleteException, RecordNotFoundException, ConstructPageException, IteratorException {
        String searchKey = VAEntry.GetApproximationString(entry.approximation, this.noOfBits);
        StringKey key = new StringKey(searchKey);
        super.Delete(key, entry.rid);
    }

    /*
     * Calculates the minimum possible approximation based on a given range and target
     * vector
     */
	
	public static BitSet GetMinimumApproximation(Vector100Dtype targetVector, Range[] ranges, int range, int bitsPerDimension, BitSet assumption) {
		double squaredRange = Math.pow(range, 2);
		double currentDistance = 0;
		Range firstRange = ranges[0];
		Range lastRange = ranges[ranges.length - 1];
		Vector100Dtype minimumVector = new Vector100Dtype();
		for (int i = 0; i < 100; i++) {
			if (currentDistance > squaredRange) {
				minimumVector.setIndex(i, (short)lastRange.upperBound);
			} else {
				int value = targetVector.getIndex(i);
				currentDistance += Math.pow(value - Range.GetMinPossibleDistance(firstRange, value), 2);
				minimumVector.setIndex(i, (short)firstRange.lowerBound);
			}
		}
		return VAEntry.CreateApproximation(minimumVector, ranges, bitsPerDimension);
	}

    /*
     * Calculates the maximum possible approximation based on a given range and target
     * vector
     */
	
	public static BitSet GetMaximumApproximation(Vector100Dtype targetVector, Range[] ranges, int range, int bitsPerDimension, BitSet assumption) {
		double squaredRange = Math.pow(range, 2);
		double currentDistance = 0;
		Range firstRange = ranges[0];
		Range lastRange = ranges[ranges.length - 1];
		Vector100Dtype maximumVector = new Vector100Dtype();
		for (int i = 0; i < 100; i++) {
			if (currentDistance > squaredRange) {
				maximumVector.setIndex(i, (short)firstRange.lowerBound);
			} else {
				int value = targetVector.getIndex(i);
				currentDistance += Math.pow(value - Range.GetMinPossibleDistance(lastRange, value), 2);
				maximumVector.setIndex(i, (short)lastRange.upperBound);
			}
		}
		return VAEntry.CreateApproximation(maximumVector, ranges, bitsPerDimension);
	}

    /***
     * TESTING FUNCTIONS
     */

    //Test function for key insertion
    public static VAEntry createEntry(int val)
    {
        Vector100Dtype vd=new Vector100Dtype();
        BitSet vectorApproximation=new BitSet();
        int i=0;
        for(i=0;i<100;i++)
        {
            vd.setIndex(i,(short)val);
        }
        Range ranges[]=VAFile.GetDefaultRanges();
        vectorApproximation=VAEntry.CreateApproximation(vd, ranges, 4);
        System.out.println("Val " + val + " key: " + VAEntry.GetApproximationString(vectorApproximation, 4));
        PageId pid=new PageId(pageNo++);
        RID rid=new RID(pid,slotNo++);
        VAEntry entry=new VAEntry(vectorApproximation,rid);
        return entry;
    }

    //This main class is only used for testing the insertion into the VA btree file
    public static void main(String[] args) throws GetFileEntryException,PinPageException,ConstructPageException
    {


        String dbPath = "minibase-db";
        SystemDefs sysdef = new SystemDefs(dbPath,100,100,"Clock");


        VAEntry[] entries = new VAEntry[101];
        for(int i = 0; i < entries.length; i++)
        {
            //entries[i] = VA_btree_file1.createEntry( i * 20000/(entries.length - 1) - 10000);

            //linear interpolation based on 'i': first entry has 10,000, last entry has -10,000
            entries[i] = createEntry(10000 - ((20000 / (entries.length - 1)) * i));
        }

        try
        {
            String fileName = "testFile7";
            VABTreeFile file=new VABTreeFile(fileName);
            BTreeHeaderPage headerPage= file.getHeaderPage();

            for(int i = 0; i < entries.length; i++)
            {
                file.insert(entries[i]);
            }

            BT.printAllLeafPages(headerPage);
            BT.printBTree(headerPage);

            Vector100Dtype target=new Vector100Dtype();
            for(int i = 0; i < 100; i++)
            {
                target.setIndex(i,(short)0);
            }

            String lowKey = "";
            String highKey = "";
            for (int i = 0; i < 100; i++) {
                // Just set the low and high key to the highest and lowest possible
                for (int j = 0; j < VAFile.BITS_PER_DIMENSTION; j++) {
                    lowKey += "0";
                    highKey += "1";
                }
            }


            System.out.println("/** NO PRUNING **/\n\n");

            VABTreeScan scanner = new VABTreeScan(file);
            scanner.VABTreeRangeScan(new StringKey(lowKey), new StringKey(highKey), target, 1000);

            System.out.println("/** PRUNING **/\n\n");

            Range[] ranges = VAFile.GetDefaultRanges(VAFile.BITS_PER_DIMENSTION);
            BitSet approx = VABTreeFile.GetMinimumApproximation(target, ranges, 1000, VAFile.BITS_PER_DIMENSTION, new BitSet());
            lowKey = VAEntry.GetApproximationString(approx, VAFile.BITS_PER_DIMENSTION);

            approx = VABTreeFile.GetMaximumApproximation(target, ranges, 1000, VAFile.BITS_PER_DIMENSTION, new BitSet());
            highKey = VAEntry.GetApproximationString(approx, VAFile.BITS_PER_DIMENSTION);

            scanner = new VABTreeScan(file);
            scanner.VABTreeRangeScan(new StringKey(lowKey), new StringKey(highKey), target, 1000);

            System.out.println("/** NEAREST NEIGHBOR **/\n\n");

            scanner = new VABTreeScan(file);
            System.out.println(scanner.VABTreeNNScan(new StringKey(lowKey), new StringKey(highKey), target, 5).size());





//            System.out.println("\n\n");
//            //Make up some low and high keys
//            String lowString = "";
//            for(int i = 0; i < 100; i++)
//            {
//			/*
//			char c;
//			if(i < 200) c = '0'; else c = '1';
//			lowString += c;*/
//
//                lowString += "0011";
//            }
//
//            String highString = "";
//            for(int i = 0; i < 100; i ++)
//            {
//			/*char c;
//			if(i < 200) c = '1'; else c = '0';
//			highString += c;*/
//                highString += "1000";
//            }
//
//            StringKey lowKey = new StringKey(lowString);
//            StringKey highKey = new StringKey(highString);

//            Range[] ranges = VAFile.GetDefaultRanges();
//            Vector100Dtype targetVector = new Vector100Dtype();
//            for (int i = 0; i < 100; i++) {
//                targetVector.setIndex(i, (short)-8000);
//            }
//            BitSet result = GetMinimumApproximation(targetVector, ranges, 5000, VAFile.BITS_PER_DIMENSTION, new BitSet());
//            System.out.println(result.toString());
//
//            result = GetMaximumApproximation(targetVector, ranges, 5000, VAFile.BITS_PER_DIMENSTION, new BitSet());
//            System.out.println(result.toString());
        }
        catch (Exception e ) {
            e.printStackTrace();
        }
    }

}
